package com.xingkeduo.config;

import java.io.Serializable;
import java.nio.charset.Charset;
import java.time.Duration;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 
 * @author GJ
 * @time 2019年1月25日 下午6:54:03
 * @description
 */
@Configuration
public class LettuceConfig {
	@Value("${spring.redis.host}")
	private String host;
	@Value("${spring.redis.password}")
	private String password;
	@Value("${spring.redis.port}")
	private int port;
	@Value("${spring.redis.database}")
	private int database;
	@Value("${spring.redis.timeout}")
	private int timeOut;
	@Value("${spring.redis.lettuce.pool.max-idle}")
	private int maxIdle;
	@Value("${spring.redis.lettuce.pool.max-active}")
	private int maxTotal;
	@Value("${spring.redis.lettuce.pool.max-wait}")
	private long maxWait;
	@Value("${spring.redis.lettuce.pool.min-idle}")
	private int minIdle;
	@Value("${spring.redis.lettuce.shutdown-timeout}")
	private int shutdownTimeout;

	@Bean(name = "redisStandaloneConfiguration")
	public RedisStandaloneConfiguration connectionAlone() {
		RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
		redisStandaloneConfiguration.setHostName(host);
		redisStandaloneConfiguration.setPort(port);
		redisStandaloneConfiguration.setDatabase(database);
		redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
		return redisStandaloneConfiguration;
	}

	// @Bean
	// public RedisStandaloneConfiguration connectionCluster() {
	// RedisClusterConfiguration redisClusterConfiguration = new
	// RedisClusterConfiguration();
	// String[] serverArray = clusterNodes.split(",");
	// Set<RedisNode> nodes = new
	// HashSet<RedisNode>();
	// for (String ipPort : serverArray) { // String[]
	// ipAndPort = ipPort.split(":"); // nodes.add(new
	// RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1]))); // } //
	// redisClusterConfiguration.setPassword(RedisPassword.of(password)); //
	// redisClusterConfiguration.setClusterNodes(nodes); //
	// redisClusterConfiguration.setMaxRedirects(maxRedirects);
	// return redisStandaloneConfiguration;
	// }

	@Bean(name = "genericObjectPoolConfig")
	public GenericObjectPoolConfig<?> poolConfig() {
		GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
		poolConfig.setMaxIdle(maxIdle);
		poolConfig.setMaxTotal(maxTotal);
		poolConfig.setMaxWaitMillis(maxWait);
		poolConfig.setMinIdle(minIdle);
		return poolConfig;
	}

	@Bean(name = "utfCharset")
	public Charset utfCharset() {
		return Charset.forName("UTF-8");
	}

	@Bean(name = "lettuceConnectionFactory")
	public LettuceConnectionFactory lettuceConnectionFactory(
			@Qualifier("redisStandaloneConfiguration") RedisStandaloneConfiguration redisStandaloneConfiguration,
			@Qualifier("genericObjectPoolConfig") GenericObjectPoolConfig<?> genericObjectPoolConfig) {

		LettuceClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
				.commandTimeout(Duration.ofSeconds(15)).poolConfig(genericObjectPoolConfig)
				.shutdownTimeout(Duration.ofSeconds(shutdownTimeout)).build();//.useSsl()

		LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration,
				lettuceClientConfiguration);
		lettuceConnectionFactory.afterPropertiesSet();
		return lettuceConnectionFactory;
	}

	@Bean(name = "databaseRedisTemplate")
	public RedisTemplate<String, Serializable> redisTemplate(
			@Qualifier("lettuceConnectionFactory") LettuceConnectionFactory lettuceConnectionFactory,
			@Qualifier("utfCharset") Charset charset) {
		Jackson2JsonRedisSerializer<Serializable> jsonSerializer = createJsonSerializer();
		RedisSerializer<String> stringSerializer = new StringRedisSerializer();

		RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
		redisTemplate.setKeySerializer(stringSerializer);
		redisTemplate.setValueSerializer(jsonSerializer);
		redisTemplate.setHashKeySerializer(jsonSerializer);
		redisTemplate.setHashValueSerializer(jsonSerializer);
		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
		return redisTemplate;
	}

	@Bean(name = "defaultRedisTemplate")
	public StringRedisTemplate getStringRedisTemplate(
			@Qualifier("lettuceConnectionFactory") LettuceConnectionFactory lettuceConnectionFactory,
			@Qualifier("utfCharset") Charset charset) {
		return new StringRedisTemplate(lettuceConnectionFactory);
	}

	private Jackson2JsonRedisSerializer<Serializable> createJsonSerializer() {
		Jackson2JsonRedisSerializer<Serializable> jsonSerializer = new Jackson2JsonRedisSerializer(Serializable.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jsonSerializer.setObjectMapper(om);

		return jsonSerializer;
	}
}
